package com.wsoft.bpm.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.wsoft.bpm.component.DynamicChainDiagramGenerator;
import com.wsoft.bpm.component.command.GetProcessCmd;
import com.wsoft.bpm.constants.BpmConstant;
import com.wsoft.bpm.entity.ActProcessMappingEntity;
import com.wsoft.bpm.entity.ActWorkflowConfigEntity;
import com.wsoft.bpm.enums.*;
import com.wsoft.bpm.mapper.ActProcessMapper;
import com.wsoft.bpm.mapper.BpmActivitiMapper;
import com.wsoft.bpm.query.mapping.ExtraBusinessDataQuery;
import com.wsoft.bpm.query.node.ProcessQuery;
import com.wsoft.bpm.query.node.TaskCandidateQuery;
import com.wsoft.bpm.query.node.TaskExecuteQuery;
import com.wsoft.bpm.query.node.TaskQuery;
import com.wsoft.bpm.query.run.AddSignQuery;
import com.wsoft.bpm.query.run.AgreeQuery;
import com.wsoft.bpm.query.run.TaskHistoryQuery;
import com.wsoft.bpm.service.*;
import com.wsoft.bpm.utils.ActivityUtils;
import com.wsoft.bpm.utils.CommonUtil;
import com.wsoft.bpm.utils.UtilMisc;
import com.wsoft.bpm.vo.*;
import com.wsoft.constant.CommonConstant;
import com.wsoft.core.exception.Asserts;
import com.wsoft.core.service.CommonFreemarkerService;
import com.wsoft.core.service.CommonMemberService;
import com.wsoft.core.service.CommonOrgService;
import com.wsoft.core.utils.LoginUserUtil;
import com.wsoft.core.utils.SqlAuthOrgHolder;
import com.wsoft.core.vo.MemberVO;
import com.wsoft.core.vo.OrganizationVO;
import com.wsoft.entity.BaseEntity;
import com.wsoft.utils.FileUtil;
import com.wsoft.vo.LoginUserVO;
import com.wsoft.vo.RoleVO;
import lombok.extern.log4j.Log4j2;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.engine.*;
import org.activiti.engine.delegate.Expression;
import org.activiti.engine.delegate.VariableScope;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.el.ExpressionManager;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskInfo;
import org.activiti.image.ProcessDiagramGenerator;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.wsoft.bpm.constants.BpmConstant.*;
import static com.wsoft.bpm.enums.ClaimStatus.*;
import static com.wsoft.bpm.enums.ExecutableOperation.*;

/**
 * @Author zhonglj
 * @Date 2024-08-28 14:15
 */
@Service
@Log4j2
public class RunProcessServiceImpl implements RunProcessService {
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private HistoryService historyService;
    @Lazy
    @Resource
    private DynamicJumpService dynamicJumpService;
    @Resource
    private ActProcessMapper actProcessMapper;
    @Resource
    private IActProcessMappingService iActProcessMappingService;
    @Resource
    private ProcessEngine processEngine;
    @Resource
    private BpmActivitiMapper bpmActivitiMapper;
    @Resource
    private ManagementService managementService;
    @Resource
    private DynamicUserTaskService dynamicUserTaskService;
    @Resource
    private ChainWorkFlowService chainWorkFlowService;
    @Resource
    private CommonMemberService commonMemberService;
    @Resource
    private CommonOrgService commonOrgService;
    @Resource
    private IActWorkflowConfigService workflowConfigService;
    @Resource
    private IActProcessMappingService actProcessMappingService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String startProcess(String processDefinitionKey, String tableName, String businessKeyColumn, String businessKey, String statusColumn, ExtraBusinessDataQuery extraData, Map map) {
        if (map == null) {
            map = new HashMap(16);
        }

        if (StrUtil.isBlank(processDefinitionKey)) {
            Asserts.fail("[bpm.process.error.run.blank.processDefinitionKey]");
        }
        if (StrUtil.isBlank(tableName)) {
            Asserts.fail("[bpm.process.error.run.blank.tableName]");
        }
        if (StrUtil.isBlank(businessKey)) {
            Asserts.fail("[bpm.process.error.run.blank.businessKey]");
        }

        LoginUserVO loginUserVO = LoginUserUtil.getUserInfo().getLoginUserVO();
        // 获取流程候选人参数
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(processDefinitionKey)
                .latestVersion()
                .singleResult();

        if (processDefinition == null) {
            Asserts.fail("[bpm.process.error.run.noneDefinition]");
        }

        // 校验流程定义的启动候选人和候选组
        checkCandidate(processDefinition.getId(), loginUserVO);

        // 校验businessKey唯一性,同一张业务表内允许重复
        long count = iActProcessMappingService.count(Wrappers.lambdaQuery(ActProcessMappingEntity.class).ne(ActProcessMappingEntity::getTableName, tableName).eq(ActProcessMappingEntity::getBusinessKey, businessKey));
        if (count > 0) {
            Asserts.fail("[bpm.process.error.run.startProcess.businessKey.exist]");
        }
        // 同一张表内检查是否流程实例已经结束
        List<ActProcessMappingEntity> processMappings = iActProcessMappingService.list(Wrappers.lambdaQuery(ActProcessMappingEntity.class).eq(ActProcessMappingEntity::getTableName, tableName).eq(ActProcessMappingEntity::getBusinessKey, businessKey));
        if (CollUtil.isNotEmpty(processMappings)) {
            List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery().processInstanceIds(processMappings.stream().map(ActProcessMappingEntity::getProcessInstantId).collect(Collectors.toSet())).list();
            if (CollUtil.isNotEmpty(processInstances)) {
                // 同一个业务下相同的业务编码尚未结束
                Asserts.fail("[bpm.process.error.run.startProcess.businessKey.exist]");
            }
        }

        // 检查业务表数据审批状态
        int approveStatus = bpmActivitiMapper.getApproveStatus(tableName, statusColumn, businessKeyColumn, businessKey);
        if (CollUtil.contains(CollUtil.newArrayList(ApproveStatus.APPROVING.getValue(), ApproveStatus.APPROVED.getValue(), ApproveStatus.REJECT.getValue(), ApproveStatus.TERMINATE.getValue()), approveStatus)) {
            Asserts.fail("[bpm.startProcess.status.submitBlocked]");
        }

        // 设置默认参数
        map.putAll(getBpmDefaultMap());
        Authentication.setAuthenticatedUserId(loginUserVO.getId().toString());
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, map);
        if (processInstance == null || processInstance.getId() == null) {
            Asserts.fail("[bpm.process.error.start.failed]");
        }

        // 录入流程-数据表对应关系
        ActProcessMappingEntity entity = new ActProcessMappingEntity();
        entity.setProcessKey(processDefinitionKey);
        entity.setProcessInstantId(processInstance.getId());
        entity.setTableName(tableName);
        entity.setStatusColumn(StrUtil.isBlank(statusColumn) ? DEFAULT_STATUS_COLUMN : statusColumn);
        entity.setBusinessKeyColumn(StrUtil.isBlank(businessKeyColumn) ? DEFAULT_BUSINESS_KEY_COLUMN : businessKeyColumn);
        entity.setBusinessKey(businessKey);
        if (extraData != null) {
            if (StrUtil.isNotBlank(extraData.getBusinessType())) {
                entity.setBusinessType(extraData.getBusinessType());
            }

            if (StrUtil.isNotBlank(extraData.getBusinessValue())) {
                entity.setBusinessValue(extraData.getBusinessValue());
            }

            if (extraData.getBusinessParam() != null) {
                entity.setBusinessParam(JSONUtil.toJsonStr(extraData.getBusinessParam()));
            }

            if (StrUtil.isNotBlank(extraData.getBusinessTitle())) {
                // 优先使用代码传递的标题
                entity.setBusinessTitle(extraData.getBusinessTitle());
            } else {
                // 获取标题配置
                ActWorkflowConfigEntity processConfig = workflowConfigService.getOne(
                        Wrappers.lambdaQuery(ActWorkflowConfigEntity.class)
                                .eq(ActWorkflowConfigEntity::getProcDefId, processDefinition.getId())
                                .eq(ActWorkflowConfigEntity::getActId, processDefinition.getKey()));
                if (processConfig != null && StrUtil.isNotBlank(processConfig.getJson())) {
                    ProcessQuery processQuery = JSONUtil.toBean(processConfig.getJson(), ProcessQuery.class);
                    if (StrUtil.equals(processQuery.getTypeOfDcTitle(), BpmConstant.DC_TITLE_TYPE_FREEMARKER)) {
                        Map dataModel = MapUtil.builder(map).putAll(extraData.getBusinessParam()).build();
                        String textTemplate = commonFreemarkerService.sendTextTemplate(processQuery.getFmTemplateCode(), dataModel, false);
                        entity.setBusinessTitle(textTemplate);
                    }
                }

            }
        }
        iActProcessMappingService.save(entity);
        // 修改数据工作流状态==>审批中
        int update = ActivityCallAble.updateApproveStatus(processInstance.getId(), businessKey, ApproveStatus.APPROVING);
        if (update <= 0) {
            Asserts.fail("[sys.msg.failed]");
        }

        return processInstance.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String startProcess(String processDefinitionKey, String tableName, String businessKey, ExtraBusinessDataQuery extraData, Map map) {
        return startProcess(processDefinitionKey, tableName, DEFAULT_BUSINESS_KEY_COLUMN, businessKey, DEFAULT_STATUS_COLUMN, extraData, map);
    }

    private void checkCandidate(String processDefinitionId, LoginUserVO loginUserVO) {
        String userId = loginUserVO.getId().toString();
        String loginOrgCode = loginUserVO.getLoginOrgCode();
        List<RoleVO> roles = loginUserVO.getRoles();

        Process process = managementService.executeCommand(new GetProcessCmd(processDefinitionId));
        List<String> userIds = process.getCandidateStarterUsers();
        List<String> groupIds = process.getCandidateStarterGroups();

        if (CollUtil.isNotEmpty(userIds) || CollUtil.isNotEmpty(groupIds)) {
            int allowedCount = 0;
            // 判断候选人是否符合
            if (CollUtil.contains(userIds, userId)) {
                allowedCount++;
            }
            // 判断候选组是否符合
            for (String group : groupIds) {
                List<String> split = StrUtil.split(group, ":");
                if (CollUtil.isEmpty(split)) {
                    continue;
                }

                String executeType = split.get(0);
                String executeValue = split.get(1);
                if (StrUtil.equals(executeType, ExecuteType.CANDIDATE_ORG.getActivitiCode())) {
                    if (StrUtil.startWith(loginOrgCode, executeValue)) {
                        allowedCount++;
                    }

                } else if (StrUtil.equals(executeType, ExecuteType.CANDIDATE_ROLE.getActivitiCode())) {
                    if (CollUtil.isNotEmpty(roles)) {
                        Optional<RoleVO> roleMatch = roles.stream().filter(e -> StrUtil.equals(e.getCode(), executeValue)).findFirst();
                        if (roleMatch.isPresent()) {
                            allowedCount++;
                        }
                    }
                }
            }

            if (allowedCount == 0) {
                Asserts.fail("[bpm.process.error.run.nonePermission]");
            }
        }
    }

    /**
     * 默认参数生成
     *
     * @param
     * @return java.util.Map
     * @author zhonglj
     * @date 2024-10-12 9:10
     **/
    private Map getBpmDefaultMap() {
        LoginUserVO loginUserVO = LoginUserUtil.getUserInfo().getLoginUserVO();
        return MapUtil.builder(new HashMap<>(16))
                .put("orgCode", loginUserVO.getLoginOrgCode())
                .put("userId", loginUserVO.getId())
                .build();
    }

    @Override
    public Task simpleAgree(String taskId, String comment) {
        Task task = getAuthTask(taskId, true);
        completeTask(task, comment);
        return task;
    }

    /**
     * 完成工作流
     *
     * @param task
     * @param comment
     * @return void
     * @author zhonglj
     * @date 2024-11-06 14:54
     **/
    private void completeTask(Task task, String comment) {
        if (StrUtil.isNotBlank(comment)) {
            taskService.addComment(task.getId(), task.getProcessInstanceId(), comment);
        }

        // 办理完成任务
        taskService.complete(task.getId());
    }

    /**
     * 校验是否自动拾取
     *
     * @param task
     * @return void
     * @author zhonglj
     * @date 2024/12/16 15:57
     **/
    private void checkClaim(Task task) {
        if (task.getAssignee() == null) {
            LoginUserVO loginUserVO = LoginUserUtil.getUserInfo().getLoginUserVO();
            // 审批人为空，判断候选人类型
            ActWorkflowConfigEntity nodeConfig = workflowConfigService.getOne(
                    Wrappers.lambdaQuery(ActWorkflowConfigEntity.class)
                            .eq(ActWorkflowConfigEntity::getActId, task.getTaskDefinitionKey())
                            .eq(ActWorkflowConfigEntity::getProcDefId, task.getProcessDefinitionId())
                            .eq(BaseEntity::getDeleted, CommonConstant.YESNO_NO)
            );

            if (nodeConfig == null || StrUtil.isBlank(nodeConfig.getJson())) {
                Asserts.fail("[bpm.claim.json]");
            }

            TaskQuery taskQuery = JSONUtil.toBean(nodeConfig.getJson(), TaskQuery.class);
            if (taskQuery != null) {
                if (getClaimStatus(task, taskQuery.getTaskCandidate()).equals(NEED_CLAIM_YET.getValue())) {
                    Asserts.fail("[bpm.claim.unClaim]");
                }
            }

            // 自动拾取任务
            taskService.claim(task.getId(), loginUserVO.getId().toString());
        }
    }

    @Override
    public Task getAuthTask(String taskId, boolean authCheck) {
        Task task = taskService.createTaskQuery().taskId(taskId).active().singleResult();
        if (task == null) {
            Asserts.fail("[bpm.process.error.empty.task]");
        }

        if (!authCheck) {
            return task;
        }
        return checkAuth(task);
    }

    @Override
    public Task getAuthTask(String processDefKey, String businessKey, boolean authCheck) {
        Task task = taskService.createTaskQuery().processDefinitionKey(processDefKey).processInstanceBusinessKey(businessKey).active().singleResult();
        if (task == null) {
            Asserts.fail("[bpm.process.error.empty.task]");
        }

        if (!authCheck) {
            return task;
        }

        return checkAuth(task);
    }

    private Task checkAuth(Task task) {
        //检查登录人是否有权限处理
        List<Task> tasks = todoList();
        // 登录人没有待办任务
        if (CollUtil.isEmpty(tasks) ||
                // 待办任务不包含当前taskId
                !tasks.stream().map(TaskInfo::getId).collect(Collectors.toList()).contains(task.getId())) {
            Asserts.fail("[bpm.process.error.permission.task.auth]");
        }

        return task;
    }

    @Override
    public String createXmlAndPngAtNowTask(String taskId, String procInstId) {
        if (StrUtil.isNotBlank(taskId)) {
            HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
            if (historicTaskInstance == null) {
                Asserts.fail("[bpm.task.error.empty]");
            }
            procInstId = historicTaskInstance.getProcessInstanceId();
        }
        try {
            //获取历史流程实例
            HistoricProcessInstance hip = historyService.createHistoricProcessInstanceQuery().processInstanceId(procInstId).singleResult();
            //获取流程中已经执行的节点，按照执行先后顺序排序
            List<HistoricActivityInstance> hai = historyService.createHistoricActivityInstanceQuery().processInstanceId(procInstId)
                    .orderByHistoricActivityInstanceId().asc().list();
            // 构造已执行的节点ID集合
            List<String> executedActivityIdList = new ArrayList<>();
            for (HistoricActivityInstance activityInstance : hai) {
                executedActivityIdList.add(activityInstance.getActivityId());
            }
            // 获取bpmnModel
            BpmnModel bpmnModel = repositoryService.getBpmnModel(hip.getProcessDefinitionId());
            Process process = managementService.executeCommand(new GetProcessCmd(hip.getProcessDefinitionId()));
            if (process != null) {
                bpmnModel.getProcesses().clear();
                bpmnModel.addProcess(process);
            }

            // 获取流程已发生流转的线ID集合
            List<String> flowIds = this.getExecutedFlows(bpmnModel, hai);
            ProcessDiagramGenerator processDiagramGenerator = new DynamicChainDiagramGenerator(procInstId);
            InputStream imageStream = processDiagramGenerator.generateDiagram(bpmnModel, executedActivityIdList, flowIds, "宋体", "宋体", "宋体", true, "png");
            return FileUtil.getImageStr(imageStream);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public void backToAct(String taskId, String comment, String actId) {
        Task currentTask = getAuthTask(taskId, true);
        if (actId == null) {
            Asserts.fail("[bpm.process.error.backToAct.empty]");
        }

        // 跳转到指定节点
        dynamicJumpService.outgoingJump(currentTask.getId(), CollUtil.newArrayList(actId), comment);
    }

    @Override
    public void claim(String taskId) {
        Task task = getAuthTask(taskId, true);
        taskService.claim(task.getId(), LoginUserUtil.getUserInfo().getLoginUserVO().getId().toString());
    }

    @Override
    public void unClaim(String taskId) {
        Task task = getAuthTask(taskId, true);
        taskService.unclaim(task.getId());
    }

    @Override
    public List<TaskHistoryVO> listApprovalNodes(String taskId, String processInstanceId) {
        if (StrUtil.isNotBlank(taskId)) {
            HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
            if (historicTaskInstance == null) {
                Asserts.fail("[bpm.task.error.empty]");
            }
            processInstanceId = historicTaskInstance.getProcessInstanceId();
        }

        List<TaskHistoryVO> historyVOS = actProcessMapper.history(CollUtil.newArrayList(processInstanceId));
        historyVOS = historyVOS.stream()
                // 过滤未审批的结果
                .filter(e -> e.getEndTime() != null)
                .filter(task -> task.getActivityId() != null)
                .collect(Collectors.toMap(
                        TaskHistoryVO::getActivityId,
                        task -> task,
                        (existing, replacement) -> existing,
                        LinkedHashMap::new
                ))
                .values()
                .stream()
                .collect(Collectors.toList());

        if (CollUtil.isNotEmpty(historyVOS)) {
            Map<String, String> actId2NameMap = historyService.createHistoricActivityInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .list()
                    .stream()
                    .filter(task -> StrUtil.isNotBlank(task.getActivityId()) && StrUtil.isNotBlank(task.getActivityName()))
                    .collect(Collectors.toMap(HistoricActivityInstance::getActivityId, HistoricActivityInstance::getActivityName, (p1, p2) -> p1));

            historyVOS.stream().forEach(task -> {
                String actName = actId2NameMap.get(task.getActivityId());
                if (StrUtil.isNotBlank(actName)) {
                    task.setName(actName);
                }
            });
        }

        return historyVOS;
    }

    @Override
    public List<DeployedDefVO> listDeployedDef() {
        List<ProcessDefinition> definitions = repositoryService.createProcessDefinitionQuery().latestVersion().list();
        return definitions.stream()
                .map(def -> new DeployedDefVO().setProcessDefKey(def.getKey()).setProcessDefName(def.getName()))
                .collect(Collectors.toList());
    }

    @Resource
    private CommonFreemarkerService commonFreemarkerService;

    @Override
    public List<TaskTodoVO> packageExtraTodoData(List<Task> tasks) {
        if (CollUtil.isNotEmpty(tasks)) {
            List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery()
                    .processInstanceIds(tasks.stream().map(TaskInfo::getProcessInstanceId).collect(Collectors.toSet()))
                    .list();
            Map<String, ProcessInstance> processInstanceMap = processInstances.stream().collect(Collectors.toMap(ProcessInstance::getProcessInstanceId, Function.identity()));
            Map<String, ActProcessMappingEntity> procInstToMappingMap = actProcessMappingService.list(
                            Wrappers.lambdaQuery(ActProcessMappingEntity.class)
                                    .in(ActProcessMappingEntity::getProcessInstantId, tasks.stream().map(TaskInfo::getProcessInstanceId).collect(Collectors.toList()))
                    ).stream()
                    .collect(Collectors.toMap(ActProcessMappingEntity::getProcessInstantId, Function.identity()));

            // 获取标题配置
            Map<String, ActWorkflowConfigEntity> procDefIdToConfig = workflowConfigService.list(
                            Wrappers.lambdaQuery(ActWorkflowConfigEntity.class)
                                    .isNotNull(ActWorkflowConfigEntity::getProcDefId)
                                    .likeRight(ActWorkflowConfigEntity::getActId, GENERATE_ID_PREFIX_PROCESS)
                    ).stream()
                    .collect(Collectors.toMap(ActWorkflowConfigEntity::getProcDefId, Function.identity(), (p1, p2) -> p1));

            return tasks.stream()
                    .filter(task -> {
                        ActProcessMappingEntity mapping = procInstToMappingMap.get(task.getProcessInstanceId());
                        return mapping != null && mapping.getDeleted() == CommonConstant.YESNO_NO;
                    })
                    .map(task -> {
                        ActWorkflowConfigEntity processConfig = procDefIdToConfig.get(task.getProcessDefinitionId());
                        ProcessQuery processQuery = null;
                        if (processConfig != null && StrUtil.isNotBlank(processConfig.getJson())) {
                            processQuery = JSONUtil.toBean(processConfig.getJson(), ProcessQuery.class);
                        }

                        TaskTodoVO convert = Convert.convert(TaskTodoVO.class, task);
                        // 设置默认标题流程名称+节点名称
                        convert.setTitle(Opt.ofNullable(processInstanceMap.get(task.getProcessInstanceId()))
                                .map(ProcessInstance::getProcessDefinitionName)
                                .orElse(""));
                        convert.setBusinessKey(processInstanceMap.get(task.getProcessInstanceId()).getBusinessKey());
                        ActProcessMappingEntity mappingEntity = procInstToMappingMap.get(task.getProcessInstanceId());
                        if (mappingEntity != null) {
                            convert.setBusinessType(mappingEntity.getBusinessType());
                            convert.setBusinessValue(mappingEntity.getBusinessValue());
                            convert.setBusinessParam(mappingEntity.getBusinessParam());
                            // 配置自定义标题
                            if (StrUtil.isNotBlank(mappingEntity.getBusinessTitle())) {
                                convert.setTitle(mappingEntity.getBusinessTitle());
                            }
                        }

                        if (processQuery != null && processQuery.isIncludeNodeName()) {
                            convert.setTitle(convert.getTitle() + "-" + task.getName());
                        }
                        return convert;
                    }).collect(Collectors.toList());
        }

        return CollUtil.newArrayList();
    }

    @Override
    public List<TaskDoneVO> packageExtraDoneData(List<HistoricTaskInstance> tasks) {
        if (CollUtil.isNotEmpty(tasks)) {
            List<HistoricProcessInstance> processInstances = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceIds(tasks.stream().map(TaskInfo::getProcessInstanceId).collect(Collectors.toSet()))
                    .list();
            Map<String, HistoricProcessInstance> processInstanceMap = processInstances.stream().collect(Collectors.toMap(HistoricProcessInstance::getId, Function.identity()));
            Map<String, ActProcessMappingEntity> procInstToMappingMap = actProcessMappingService.list(
                            Wrappers.lambdaQuery(ActProcessMappingEntity.class)
                                    .in(ActProcessMappingEntity::getProcessInstantId, tasks.stream().map(TaskInfo::getProcessInstanceId).collect(Collectors.toList()))
                    ).stream()
                    .collect(Collectors.toMap(ActProcessMappingEntity::getProcessInstantId, Function.identity()));

            // 获取标题配置
            Map<String, ActWorkflowConfigEntity> procDefIdToConfig = workflowConfigService.list(
                            Wrappers.lambdaQuery(ActWorkflowConfigEntity.class)
                                    .isNotNull(ActWorkflowConfigEntity::getProcDefId)
                                    .likeRight(ActWorkflowConfigEntity::getActId, GENERATE_ID_PREFIX_PROCESS)
                    ).stream()
                    .collect(Collectors.toMap(ActWorkflowConfigEntity::getProcDefId, Function.identity(), (p1, p2) -> p1));

            return tasks.stream()
                    .filter(task -> {
                        ActProcessMappingEntity mapping = procInstToMappingMap.get(task.getProcessInstanceId());
                        return mapping != null && mapping.getDeleted() == CommonConstant.YESNO_NO;
                    })
                    .map(task -> {
                        ActWorkflowConfigEntity processConfig = procDefIdToConfig.get(task.getProcessDefinitionId());
                        ProcessQuery processQuery = null;
                        if (processConfig != null && StrUtil.isNotBlank(processConfig.getJson())) {
                            processQuery = JSONUtil.toBean(processConfig.getJson(), ProcessQuery.class);
                        }

                        TaskDoneVO convert = Convert.convert(TaskDoneVO.class, task);
                        convert.setCreateTime(LocalDateTimeUtil.of(convert.getStartTime()));
                        // 设置默认标题流程名称+节点名称
                        convert.setTitle(Opt.ofBlankAble(processInstanceMap.get(task.getProcessInstanceId()))
                                .map(HistoricProcessInstance::getProcessDefinitionName)
                                .orElse(""));
                        convert.setBusinessKey(processInstanceMap.get(task.getProcessInstanceId()).getBusinessKey());
                        ActProcessMappingEntity mappingEntity = procInstToMappingMap.get(task.getProcessInstanceId());
                        if (mappingEntity != null) {
                            convert.setBusinessType(mappingEntity.getBusinessType());
                            convert.setBusinessValue(mappingEntity.getBusinessValue());
                            convert.setBusinessParam(mappingEntity.getBusinessParam());
                            // 配置自定义标题
                            if (StrUtil.isNotBlank(mappingEntity.getBusinessTitle())) {
                                convert.setTitle(mappingEntity.getBusinessTitle());
                            }
                        }

                        if (processQuery != null && processQuery.isIncludeNodeName()) {
                            convert.setTitle(convert.getTitle() + "-" + task.getName());
                        }
                        return convert;
                    }).collect(Collectors.toList());
        }

        return CollUtil.newArrayList();
    }

    @Override
    public void deleteProcessInstance(String processInstanceId, String deleteReason) {
        // 先主动完成ru_task中的动态任务
        taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .active()
                .list()
                .stream()
                // 过滤所有动态节点
                .filter(task -> StrUtil.startWith(task.getCategory(), DYNAMIC_NODE))
                .forEach(task -> taskService.complete(task.getId()));

        // 工作流删除流程
        runtimeService.deleteProcessInstance(processInstanceId, deleteReason);
    }

    /**
     * 获取流程已发生流转的线ID集合
     *
     * @param bpmnModel
     * @param historicActivityInstances 历史流程实例list
     * @return
     */
    private List<String> getExecutedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) {
        //流转线ID集合
        List<String> flowIdList = new ArrayList<String>();
        //全部活动实例
        List<FlowNode> historicFlowNodeList = new LinkedList<FlowNode>();
        //已完成的历史活动节点
        List<HistoricActivityInstance> finishedActivityInstanceList = new LinkedList<HistoricActivityInstance>();
        for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
            historicFlowNodeList.add((FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true));
            if (historicActivityInstance.getEndTime() != null) {
                finishedActivityInstanceList.add(historicActivityInstance);
            }
        }
        // 遍历已完成的活动实例，从每个实例的outgoingFlows中找到已执行的
        FlowNode currentFlowNode = null;
        for (HistoricActivityInstance currentActivityInstance : finishedActivityInstanceList) {
            // 获得当前活动对应的节点信息及outgoingFlows信息
            currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);
            List<SequenceFlow> sequenceFlowList = currentFlowNode.getOutgoingFlows();
            /**
             * 遍历outgoingFlows并找到已流转的
             * 满足如下条件任务已流转：
             * 1.当前节点是并行网关或包含网关，则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转
             * 2.当前节点是以上两种类型之外的，通过outgoingFlows查找到的时间最近的流转节点视为有效流转
             */
            FlowNode targetFlowNode = null;
            if (ActivityType.PARALLEL_GATEWAY.getType().equals(currentActivityInstance.getActivityType())
                    || ActivityType.INCLUSIVE_GATEWAY.getType().equals(currentActivityInstance.getActivityType())) {
                for (SequenceFlow sequenceFlow : sequenceFlowList) {
                    //遍历历史活动节点，找到匹配Flow目标节点的
                    targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(), true);
                    if (historicFlowNodeList.contains(targetFlowNode)) {
                        flowIdList.add(sequenceFlow.getId());
                    }
                }

            } else {
                List<Map<String, String>> tempMapList = new LinkedList<Map<String, String>>();
                for (SequenceFlow sequenceFlow : sequenceFlowList) {
                    //遍历历史活动节点，找到匹配Flow目标节点的
                    for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
                        if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {
                            tempMapList.add(UtilMisc.toMap("flowId", sequenceFlow.getId(), "activityStartTime", String.valueOf(historicActivityInstance.getStartTime().getTime())));
                        }
                    }

                }
                String flowId = null;
                for (Map<String, String> map : tempMapList) {
                    flowId = map.get("flowId");
                    flowIdList.add(flowId);
                }
            }
        }
        return flowIdList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void agree(AgreeQuery query) {
        Task currentTask = getAuthTask(query.getTaskId(), query.getAuthCheck());
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(currentTask.getProcessDefinitionId())
                .singleResult();
        ActivityCallAble callService = ActivityCallAble.findCallService(processDefinition.getKey());
        if (ActivityUtils.isMultiTask(currentTask)) {
            callService.beforeAgree(currentTask.getBusinessKey(), currentTask.getId(), currentTask.getTaskDefinitionKey(), currentTask.getName());
            // 设置同意数
            setApprovalCount(currentTask, ActivityVariable.SIGN_AGREE_COUNT_KEY.getValue());
            // 会签审批
            completeTask(currentTask, query.getComment());

        } else if (ActivityUtils.isContinuousSupervisor(currentTask)) {
            // 连续上级
            dynamicUserTaskService.serialAgree(currentTask, query.getComment());

        } else if (ActivityUtils.isAddSign(currentTask)) {
            // 加签审批
            dynamicUserTaskService.parallelAgree(currentTask, query.getComment());

        } else if (ActivityUtils.isDynamicChain(currentTask)) {
            // 动态审批链审批
            chainWorkFlowService.agree(currentTask, query.getComment());

        } else {
            callService.beforeAgree(currentTask.getBusinessKey(), currentTask.getId(), currentTask.getTaskDefinitionKey(), currentTask.getName());
            // 其他情况默认走一般同意
            agreeNormal(currentTask, query.getNextActIds(), query.getComment(), query.getTaskExecute(), query.getTaskCandidate());
        }
    }

    private void setApprovalCount(Task currentTask, String keyPrefix) {
        String taskId = currentTask.getId();
        // 指定操作
        String varKeyPrefix = keyPrefix + currentTask.getTaskDefinitionKey();
        Integer count = taskService.getVariable(taskId, varKeyPrefix, Integer.class);
        if (count == null) {
            count = 1;
        } else {
            count++;
        }
        taskService.setVariable(taskId, varKeyPrefix, count);

        // 已完成
        String varCompleted = ActivityVariable.SIGN_COMPLETED_COUNT_KEY.getValue() + currentTask.getTaskDefinitionKey();
        Integer countCompleted = taskService.getVariable(taskId, varCompleted, Integer.class);
        if (countCompleted == null) {
            countCompleted = 1;
        } else {
            countCompleted++;
        }
        taskService.setVariable(taskId, varCompleted, countCompleted);
    }

    @Override
    public void refuse(String taskId, String comment, boolean authCheck) {
        Task currentTask = getAuthTask(taskId, authCheck);
        if (ActivityUtils.isMultiTask(currentTask)) {
            // 设置拒绝数
            setApprovalCount(currentTask, ActivityVariable.SIGN_REFUSE_COUNT_KEY.getValue());
            // 多实例审批
            completeTask(currentTask, comment);
        } else if (ActivityUtils.isContinuousSupervisor(currentTask)) {
            // 连续上级
            dynamicUserTaskService.serialRefuse(currentTask, comment);
        } else if (ActivityUtils.isAddSign(currentTask)) {
            // 加签审批
            dynamicUserTaskService.parallelRefuse(currentTask, comment);
        } else if (ActivityUtils.isDynamicChain(currentTask)) {
            //  动态审批链
            chainWorkFlowService.refuse(currentTask, comment);
        } else {
            checkClaim(currentTask);
            // 其他情况默认回退一步
            backOne(currentTask.getId(), comment);
        }
    }

    /**
     * 普通同意,需要设置后续节点审批人
     *
     * @param currentTask
     * @param nextActIds
     * @param comment
     * @param taskExecute
     * @param taskCandidate
     * @return void
     * @author zhonglj
     * @date 2024/12/7 8:48
     **/
    private void agreeNormal(Task currentTask, List<String> nextActIds, String comment, TaskExecuteQuery taskExecute, TaskCandidateQuery taskCandidate) {
        String executionId = currentTask.getExecutionId();
        String processInstanceId = currentTask.getProcessInstanceId();

        // 判断是正常走流程还是跳转逻辑
        if (CollUtil.isNotEmpty(nextActIds)) {
            // 工作流跳转
            dynamicJumpService.outgoingJump(
                    currentTask.getId(),
                    nextActIds,
                    comment
            );

        } else {
            checkClaim(currentTask);
            // 完成任务
            completeTask(currentTask, comment);
        }

        setNextNodeInfo(taskExecute, taskCandidate, executionId, processInstanceId, nextActIds);
    }

    /**
     * 设置下一个节点审批信息
     *
     * @param taskExecute
     * @param taskCandidate
     * @param executionId
     * @param processInstanceId
     * @return void
     * @author zhonglj
     * @date 2024/12/7 8:57
     **/
    private void setNextNodeInfo(TaskExecuteQuery taskExecute, TaskCandidateQuery taskCandidate, String executionId, String processInstanceId, List<String> nextActIds) {
        List<Task> nextTasks = null;
        if (CollUtil.isNotEmpty(nextActIds)) {
            // 根据下一个节点id列表获取任务
            nextTasks = taskService.createTaskQuery().processInstanceId(processInstanceId).active().list().stream().filter(e -> CollUtil.contains(nextActIds, e.getTaskDefinitionKey())).collect(Collectors.toList());

        } else {
            nextTasks = taskService.createTaskQuery().processInstanceId(processInstanceId).active().list().stream()
                    // 通过执行路径来限定用户任务
                    .filter(task -> StrUtil.equals(executionId, task.getExecutionId())).collect(Collectors.toList());
        }

        if (CollUtil.isNotEmpty(nextTasks)) {
            for (Task nextTask : nextTasks) {
                ActWorkflowConfigEntity config = workflowConfigService.getOne(Wrappers.lambdaQuery(ActWorkflowConfigEntity.class).eq(ActWorkflowConfigEntity::getProcDefId, nextTask.getProcessDefinitionId()).eq(ActWorkflowConfigEntity::getActId, nextTask.getTaskDefinitionKey()));
                if (config != null && StrUtil.isNotBlank(config.getJson())) {
                    TaskQuery taskQuery = JSONUtil.toBean(config.getJson(), TaskQuery.class);
                    String dynamicApprovalType = taskQuery.getDynamicApprovalType();
                    String dynamicApprovalValue = taskQuery.getDynamicApprovalValue();
                    if (StrUtil.isBlank(dynamicApprovalType)) {
                        continue;
                    }

                    if (StrUtil.equals(dynamicApprovalType, DynamicApprovalType.OPTIONAL_PERSONNEL.getValue())) {
                        if (taskExecute == null) {
                            Asserts.fail("[bpm.nextNode.optionalPerson.empty]");
                        }
                        optionalPerson(nextTask, taskExecute, taskCandidate);
                    } else {
                        dynamicPerson(nextTask, dynamicApprovalType, dynamicApprovalValue);
                    }
                }
            }
        }
    }

    private void dynamicPerson(Task nextTask, String dynamicApprovalType, String dynamicApprovalValue) {
        if (StrUtil.equals(dynamicApprovalType, DynamicApprovalType.DEPARTMENT_HEAD.getValue())) {
            // 部门负责人
            SqlAuthOrgHolder.open();
            OrganizationVO orgInfo = commonOrgService.getOrgInfo(Long.valueOf(dynamicApprovalValue));
            SqlAuthOrgHolder.close();
            Long orgLeaderId = orgInfo.getMemberId();
            if (orgLeaderId != null) {
                taskService.setAssignee(nextTask.getId(), orgLeaderId.toString());
            }

        } else {
            // 上一个用户节点审批人
            Process process = managementService.executeCommand(new GetProcessCmd(nextTask.getProcessDefinitionId()));
            FlowElement flowElement = process.getFlowElement(nextTask.getTaskDefinitionKey());
            List<FlowElement> preFirstUserTask = ActivityUtils.getPreFirstUserTask(flowElement);
            if (CollUtil.isEmpty(preFirstUserTask)) {
                Asserts.fail("[bpm.dynamic.preUser.empty]");
            }
            String taskDefKey = preFirstUserTask.get(0).getId();
            // 获取上一个节点的审批历史
            List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().processInstanceId(nextTask.getProcessInstanceId()).taskDefinitionKey(taskDefKey).orderByTaskCreateTime().desc().list();
            if (CollUtil.isEmpty(list)) {
                Asserts.fail("[bpm.dynamic.preTask.empty]");
            }
            HistoricTaskInstance lastTask = list.get(0);
            // 上一节点审批人
            String lastAssign = lastTask.getAssignee();

            if (StrUtil.equals(dynamicApprovalType, DynamicApprovalType.ASSIGNED_SUPERVISOR.getValue())) {
                int countDown = Integer.valueOf(dynamicApprovalValue);
                Long targetLeaderId = null;
                while (countDown > 0) {
                    MemberVO memberInfo = commonMemberService.getMemberInfo(Long.valueOf(lastAssign));
                    Long leaderId = memberInfo.getMemberLeaderId();
                    if (leaderId != null) {
                        targetLeaderId = leaderId;
                    } else {
                        // 倒计时未结束，先找到了为空的领导id,循环无法继续
                        break;
                    }
                    // 倒计时-1
                    countDown--;

                }

                if (targetLeaderId != null) {
                    // 设置审批人
                    taskService.setAssignee(nextTask.getId(), targetLeaderId.toString());
                } else {
                    Asserts.fail("[bpm.process.error.dynamicPerson.empty]");
                }

            } else if (StrUtil.equals(dynamicApprovalType, DynamicApprovalType.CONTINUOUS_SUPERIOR.getValue())) {
                int countDown = Integer.valueOf(dynamicApprovalValue);
                List<String> dynamicLeaderIdList = new ArrayList<>();
                while (countDown > 0) {
                    MemberVO memberInfo = commonMemberService.getMemberInfo(Long.valueOf(lastAssign));
                    Long leaderId = memberInfo.getMemberLeaderId();
                    if (leaderId != null) {
                        dynamicLeaderIdList.add(leaderId.toString());
                    } else {
                        // 倒计时未结束，先找到了为空的领导id,循环无法继续
                        break;
                    }
                    // 倒计时-1
                    countDown--;

                }

                // 后续节点拼接一个动态顺序审批链
                if (CollUtil.isNotEmpty(dynamicLeaderIdList)) {
                    taskService.setAssignee(nextTask.getId(), DYNAMIC_ASSIGNEE_LEADER);
                    dynamicUserTaskService.addDynamicSerialNode(nextTask.getId(), dynamicLeaderIdList, "上级领导");
                } else {
                    Asserts.fail("获取连续上级为空!");
                }

            }
        }

    }

    private void optionalPerson(Task nextTask, TaskExecuteQuery taskExecute, TaskCandidateQuery taskCandidate) {
        // 后续任务设置为指定审批人
        String currentTaskId = nextTask.getId();
        // 任务执行人
        if (taskExecute != null) {
            if (StrUtil.isNotBlank(taskExecute.getExpression())) {
                taskService.setAssignee(currentTaskId, taskExecute.getExpression());
            }

            if (StrUtil.isNotBlank(taskExecute.getAssignee())) {
                taskService.setAssignee(currentTaskId, taskExecute.getAssignee());
            }
        }

        // 任务候选人
        if (taskCandidate != null) {
            // 候选人
            if (CollUtil.isNotEmpty(taskCandidate.getCandidateUser())) {
                List<String> candidateUsers = taskCandidate.getCandidateUser();
                candidateUsers.stream().forEach(user -> taskService.addCandidateUser(currentTaskId, user));
            }

            // 候选组
            if (CollUtil.isNotEmpty(taskCandidate.getCandidateOrg())) {
                StrUtil.split(ExecuteType.getCandidateGroups(ExecuteType.CANDIDATE_ORG.getValue(), taskCandidate.getCandidateOrg()), ",").stream().forEach(group -> taskService.addCandidateGroup(currentTaskId, group));

            }
            if (CollUtil.isNotEmpty(taskCandidate.getCandidateRole())) {
                StrUtil.split(ExecuteType.getCandidateGroups(ExecuteType.CANDIDATE_ROLE.getValue(), taskCandidate.getCandidateRole()), ",").stream().forEach(group -> taskService.addCandidateGroup(currentTaskId, group));
            }
            if (CollUtil.isNotEmpty(taskCandidate.getCandidateLevel())) {
                StrUtil.split(ExecuteType.getCandidateGroups(ExecuteType.CANDIDATE_LEVEL.getValue(), taskCandidate.getCandidateLevel()), ",").stream().forEach(group -> taskService.addCandidateGroup(currentTaskId, group));
            }
            if (CollUtil.isNotEmpty(taskCandidate.getCandidatePosition())) {
                StrUtil.split(ExecuteType.getCandidateGroups(ExecuteType.CANDIDATE_POSITION.getValue(), taskCandidate.getCandidatePosition()), ",").stream().forEach(group -> taskService.addCandidateGroup(currentTaskId, group));
            }
        }
    }

    @Override
    public void backOne(String taskId, String comment) {
        backOne(taskId, comment, true);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void backOne(String taskId, String comment, boolean authCheck) {
        Task currentTask = getAuthTask(taskId, authCheck);
        HistoricActivityInstance lastNode = getLastNode(currentTask.getProcessInstanceId(), null);
        if (lastNode == null) {
            Asserts.fail("[bpm.process.error.backOne.empty]");
        }
        // 增加判断是连续上级判断
        if (isDynamicLeaderNode(lastNode)) {
            lastNode = getLastNode(currentTask.getProcessInstanceId(), lastNode.getActivityId());
            if (lastNode == null) {
                Asserts.fail("[bpm.process.error.backOne.empty]");
            }
        }

        // 返回上一个节点
        dynamicJumpService.outgoingJump(currentTask.getId(), CollUtil.newArrayList(lastNode.getActivityId()), comment);
    }

    /**
     * 是否为连续上级节点
     * 连续上级获取的是上一个审批节点的连续上级，当前节点没有审批人，再往前退一步
     *
     * @param lastNode
     * @return boolean
     * @author zhonglj
     * @date 2024/12/13 10:58
     **/
    private boolean isDynamicLeaderNode(HistoricActivityInstance lastNode) {
        ActWorkflowConfigEntity config = workflowConfigService.getOne(Wrappers.lambdaQuery(ActWorkflowConfigEntity.class).eq(ActWorkflowConfigEntity::getProcDefId, lastNode.getProcessDefinitionId()).eq(ActWorkflowConfigEntity::getActId, lastNode.getActivityId()));
        if (config == null || StrUtil.isBlank(config.getJson())) {
            return false;
        }

        TaskQuery taskQuery = JSONUtil.toBean(config.getJson(), TaskQuery.class);
        // 是否为连续上级
        return StrUtil.equals(taskQuery.getDynamicApprovalType(), DynamicApprovalType.CONTINUOUS_SUPERIOR.getValue());
    }

    private HistoricActivityInstance getLastNode(String processInstanceId, String curActivityId) {
        // 获取所有历史任务（按创建时间降序）
        List<HistoricTaskInstance> hisTaskList = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).orderByTaskCreateTime().desc().list().stream()
                // 过滤动态节点
                .filter(ht -> !StrUtil.startWith(ht.getCategory(), DYNAMIC_NODE)).collect(Collectors.toList());

        // 获取所有历史活动
        List<HistoricActivityInstance> hisActivityList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list();

        // 判断是否能够退回
        if (CollUtil.isEmpty(hisTaskList) || hisTaskList.size() == 1) {
            return null;
        }

        // 当前任务
        HistoricTaskInstance currentTask = hisTaskList.get(0);
        if (StrUtil.isNotBlank(curActivityId)) {
            // 从指定位置退回
            for (int i = 1; i < hisTaskList.size(); i++) {
                HistoricTaskInstance historicTaskInstance = hisTaskList.get(0);
                if (StrUtil.equals(historicTaskInstance.getTaskDefinitionKey(), curActivityId)) {
                    currentTask = historicTaskInstance;
                    break;
                }
            }

            // 没有找到指定的节点
            return null;
        }

        // 排除连续退回导致的问题
        int currentIndex = 0;
        for (int i = 1; i < hisTaskList.size(); i++) {
            HistoricTaskInstance eachTask = hisTaskList.get(i);
            if (eachTask.getTaskDefinitionKey().equals(currentTask.getTaskDefinitionKey())) {
                currentIndex = i;
            }
        }

        if (currentIndex + 1 >= hisTaskList.size()) {
            // 如果获取的上一个节点超出审批记录,已经退回到第一个审批人了
            return null;
        }

        // 前一个任务
        HistoricTaskInstance lastTask = hisTaskList.get(currentIndex + 1);
        // 前一个活动
        HistoricActivityInstance lastActivity = hisActivityList.stream().filter(e -> lastTask.getId().equals(e.getTaskId())).collect(Collectors.toList()).get(0);
        return lastActivity;
    }

    @Override
    public void setAssign(String taskId, String assignee) {
        Task currentTask = getAuthTask(taskId, true);
        if (StrUtil.isBlank(currentTask.getAssignee()) || StrUtil.isBlank(assignee)) {
            Asserts.fail("[bpm.process.error.setAssign.empty]");
        }

        Long oldAssign = Long.valueOf(currentTask.getAssignee());
        Long newAssign = Long.valueOf(assignee);
        Map<Long, String> assignToName = commonMemberService.getMemberListByName(
                        CollUtil.newArrayList(oldAssign, newAssign),
                        null, null, null)
                .stream()
                .collect(Collectors.toMap(MemberVO::getId, MemberVO::getName));
        // 增加一条转签记录
        String record = StrUtil.format("【{}】转签给【{}】", assignToName.get(oldAssign), assignToName.get(newAssign));
        taskService.addComment(currentTask.getId(), currentTask.getProcessInstanceId(), record);
        taskService.setAssignee(currentTask.getId(), assignee);
    }

    @Override
    public List<TaskHistoryVO> history(TaskHistoryQuery query) {
        HistoricProcessInstance processInstance = null;
        if (StrUtil.isNotBlank(query.getBusinessKey())) {
            List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceBusinessKey(query.getBusinessKey())
                    .orderByProcessInstanceStartTime().desc()
                    .list();
            if (CollUtil.isNotEmpty(list)) {
                processInstance = list.get(0);
            }

        } else if (StrUtil.isNotBlank(query.getTaskId())) {
            HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(query.getTaskId()).singleResult();
            if (historicTaskInstance == null) {
                Asserts.fail("[bpm.task.error.empty]");
            }
            processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(historicTaskInstance.getProcessInstanceId()).singleResult();

        } else if (StrUtil.isNotBlank(query.getProcessInstanceId())) {
            processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(query.getProcessInstanceId()).singleResult();

        }

        if (processInstance == null) {
            return CollUtil.newArrayList();
        }

        // 获取流程配置
        ActWorkflowConfigEntity config = workflowConfigService.getOne(Wrappers.lambdaQuery(ActWorkflowConfigEntity.class).eq(ActWorkflowConfigEntity::getProcDefId, processInstance.getProcessDefinitionId()).eq(ActWorkflowConfigEntity::getActId, processInstance.getProcessDefinitionKey()));
        boolean includePreSubmitApprovals = false;
        if (config != null && StrUtil.isNotBlank(config.getJson())) {
            ProcessQuery processQuery = JSONUtil.toBean(config.getJson(), ProcessQuery.class);
            includePreSubmitApprovals = processQuery.isIncludePreSubmitApprovals();
        }


        List<String> procInstIds = CollUtil.newArrayList();
        if (includePreSubmitApprovals) {
            // 查询业务编码关联的流程实例
            procInstIds.addAll(historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(processInstance.getBusinessKey()).list().stream().map(HistoricProcessInstance::getId).collect(Collectors.toList()));

        } else {
            procInstIds.add(processInstance.getId());
        }

        List<TaskHistoryVO> historyVOS = actProcessMapper.history(procInstIds);
        if (CollUtil.isNotEmpty(historyVOS)) {
            Map<Long, String> id2NameMap = Opt.ofNullable(commonMemberService.getMemberList()).orElse(CollUtil.newArrayList())
                    .stream().collect(Collectors.toMap(MemberVO::getId, MemberVO::getName));
            historyVOS.stream().forEach(e -> {
                if (NumberUtil.isNumber(e.getAssignee())) {
                    e.setAssignee(id2NameMap.get(Long.valueOf(e.getAssignee())));
                }
                e.setDuration(CommonUtil.formatDuration(Long.valueOf(Opt.ofNullable(e.getDuration()).orElse("0"))));
            });
        }
        return historyVOS;
    }


    @Override
    public void rollback(String taskId, String comment) {
        rollback(taskId, comment, true);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rollback(String taskId, String comment, boolean authCheck) {
        Task task = getAuthTask(taskId, authCheck);
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();

        if (StrUtil.isNotBlank(comment)) {
            taskService.addComment(task.getId(), task.getProcessInstanceId(), comment);
        }

        deleteProcessInstance(task.getProcessInstanceId(), comment);

        //回调退回
        ActivityCallAble callService = ActivityCallAble.findCallService(processDefinition.getKey());
        if (callService != null) {
            callService.callBack(processDefinition.getKey(), task.getProcessInstanceId(), task.getBusinessKey());
        }
    }

    @Override
    public List<Task> todoList() {
        LoginUserVO loginUserVO = LoginUserUtil.getUserInfo().getLoginUserVO();
        List<String> groups = new ArrayList<>();

        // 拼接组织
        groups.add(ExecuteType.getCandidateGroups(ExecuteType.CANDIDATE_ORG.getValue(), CollUtil.newArrayList(loginUserVO.getLoginOrgId().toString())));

        // 拼接角色
        List<RoleVO> roles = loginUserVO.getRoles();
        if (CollUtil.isNotEmpty(roles)) {
            groups.addAll(roles.stream().map(role -> ExecuteType.getCandidateGroups(ExecuteType.CANDIDATE_ROLE.getValue(), CollUtil.newArrayList(role.getId().toString()))).collect(Collectors.toList()));
        }

        // 拼接岗位
        Long curPostId = loginUserVO.getCurPostId();
        if (curPostId != null) {
            groups.add(ExecuteType.getCandidateGroups(ExecuteType.CANDIDATE_POSITION.getValue(), CollUtil.newArrayList(curPostId.toString())));
        }

        // 拼接职务级别
        Long levelId = loginUserVO.getLevelId();
        if (levelId != null) {
            groups.add(ExecuteType.getCandidateGroups(ExecuteType.CANDIDATE_LEVEL.getValue(), CollUtil.newArrayList(levelId.toString())));
        }

        // 开始待办查询
        return taskService.createTaskQuery().taskCandidateOrAssigned(loginUserVO.getId().toString(), groups).orderByTaskCreateTime().desc().list();
    }

    /**
     * 按照每3个长度作为一层返回所有层级的组织代码
     *
     * @param userGroupCode
     * @return java.util.List<java.lang.String>
     * @author zhonglj
     * @date 2024-11-19 9:32
     **/
    private List<String> generateGroupCodes(String userGroupCode) {
        List<String> groupCodes = new ArrayList<>();
        String currentGroupCode = userGroupCode;

        while (currentGroupCode.length() > 0) {
            // Add the current group code to the list
            groupCodes.add(currentGroupCode);

            // Remove the last 3 characters to get the parent group code
            if (currentGroupCode.length() > 3) {
                currentGroupCode = currentGroupCode.substring(0, currentGroupCode.length() - 3);
            } else {
                // If the group code is less than or equal to 3 characters, it's the top level
                break;
            }
        }

        return groupCodes;
    }

    @Override
    public List<HistoricTaskInstance> doneList() {
        LoginUserVO loginUserVO = LoginUserUtil.getUserInfo().getLoginUserVO();
        HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery()
                // 查询指定用户的任务
                .taskAssignee(loginUserVO.getId().toString())
                // 查询已完成的任务
                .finished()
                // 按任务结束时间排序
                .orderByHistoricTaskInstanceEndTime()
                // 降序排列
                .desc();

        return query.list();
    }

    @Override
    public void stopProcess(String processInstanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (processInstance != null) {
            // 强制结束流程，通过删除理由来区分
            deleteProcessInstance(processInstanceId, STOP_PROCESS_INSTANCE_FORCEFULLY);
        }
    }

    @Override
    public void addSign(AddSignQuery signQuery) {
        String type = signQuery.getType();
        String taskId = signQuery.getTaskId();
        List<String> assigns = signQuery.getAssigns();
        Task task = getAuthTask(taskId, true);
        if (task == null) {
            Asserts.fail("[bpm.process.error.empty.task]");
        }

        // 来源节点为当前任务
        String sourceTaskId = task.getId();
        String originTaskId = task.getId();
        if (task.getParentTaskId() != null) {
            String oriTaskId = taskService.getVariableLocal(task.getParentTaskId(), DYNAMIC_ORIGIN_TASK_ID, String.class);
            if (StrUtil.isNotBlank(oriTaskId)) {
                originTaskId = oriTaskId;
            }
        }

        String taskName = task.getName();
        if (StrUtil.equals(APPROVAL_THEN_SIGN.getValue(), type)) {
            String message = Opt.ofBlankAble(signQuery.getComment()).orElse(StrUtil.format("当前操作【{}】自动同意", APPROVAL_THEN_SIGN.getDescription()));
            // 加签人后审,添加一条当前节点审批记录
            taskService.addComment(taskId, task.getProcessInstanceId(), message);
            taskName += "-" + APPROVAL_THEN_SIGN.getDescription();
            // 保留加签所有的请求参数
            taskService.setVariableLocal(taskId, DYNAMIC_ASSIGN_QUERY_DATA, JSONUtil.toJsonStr(signQuery));

        } else if (StrUtil.equals(SIGN_THEN_APPROVAL.getValue(), type)) {
            taskName += "-" + SIGN_THEN_APPROVAL.getDescription();
        }

        // 逻辑上已经审批完成了，设置加签常量作为标识
        taskService.setAssignee(taskId, DYNAMIC_ASSIGNEE_SIGN);
        // 保留来源节点审批人
        taskService.setVariableLocal(taskId, DYNAMIC_ASSIGN_OWNER, task.getAssignee());

        // 添加动态并行节点，开始加签
        dynamicUserTaskService.addDynamicParallelNode(originTaskId, sourceTaskId, assigns, type, taskName);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reject(String taskId, String comment) {
        Task task = getAuthTask(taskId, true);
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();

        taskService.addComment(task.getId(), task.getProcessInstanceId(), comment);
        deleteProcessInstance(task.getProcessInstanceId(), comment);

        //回调驳回
        ActivityCallAble callService = ActivityCallAble.findCallService(processDefinition.getKey());
        if (callService != null) {
            callService.callReject(processDefinition.getKey(), task.getProcessInstanceId(), task.getBusinessKey());
        }
    }

    @Override
    public ApprovalNodeDataVO getNodeData(String taskId) {
        ApprovalNodeDataVO respVO = new ApprovalNodeDataVO();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
        if (task == null && historicTaskInstance == null) {
            Asserts.fail("[bpm.process.error.empty.task]");
        }

        String procInstId = Opt.ofNullable(task).map(Task::getProcessInstanceId)
                .orElseGet(() -> Opt.ofNullable(historicTaskInstance).map(HistoricTaskInstance::getProcessInstanceId)
                        .orElse(null));

        // 获取业务主键
        ActProcessMappingEntity processMapping = actProcessMappingService.getOne(Wrappers.lambdaQuery(ActProcessMappingEntity.class).eq(ActProcessMappingEntity::getProcessInstantId, procInstId));
        if (processMapping != null) {
            respVO.setBusinessKey(processMapping.getBusinessKey());
            respVO.setBusinessType(processMapping.getBusinessType());
            respVO.setBusinessValue(processMapping.getBusinessValue());
            respVO.setBusinessParam(processMapping.getBusinessParam());

            String idColumn = bpmActivitiMapper.selectPrimaryKey(processMapping.getTableName(), "wsoft");
            if (StrUtil.isNotBlank(idColumn)) {
                // 查询指定表单主键
                String keyColumnVal = bpmActivitiMapper.getKeyColumnVal(processMapping.getTableName(), idColumn, processMapping.getBusinessKeyColumn(), processMapping.getBusinessKey());
                respVO.setId(keyColumnVal);
            }
        }

        if (task != null) {
            // 获取待办节点信息
            ActWorkflowConfigEntity nodeConfig = workflowConfigService.getOne(Wrappers.lambdaQuery(ActWorkflowConfigEntity.class).eq(ActWorkflowConfigEntity::getActId, task.getTaskDefinitionKey()).eq(ActWorkflowConfigEntity::getProcDefId, task.getProcessDefinitionId()).eq(BaseEntity::getDeleted, CommonConstant.YESNO_NO));
            if (nodeConfig != null && StrUtil.isNotBlank(nodeConfig.getJson())) {
                TaskQuery taskQuery = JSONUtil.toBean(nodeConfig.getJson(), TaskQuery.class);
                if (taskQuery != null) {
                    // 拾取状态
                    respVO.setClaimStatus(getClaimStatus(task, taskQuery.getTaskCandidate()));
                    // 页面操作
                    boolean multiTask = ActivityUtils.isMultiTask(task);
                    boolean addSign = ActivityUtils.isAddSign(task);
                    boolean continuousSupervisor = ActivityUtils.isContinuousSupervisor(task);
                    boolean dynamicChain = ActivityUtils.isDynamicChain(task);
                    if (multiTask) {
                        // 会签节点：同意，拒绝、转签、知会
                        respVO.setOperations(CollUtil.newArrayList(AGREE.getValue(), REFUSE.getValue(), ASSIGN.getValue(), NOTICE.getValue()));
                    } else if (addSign) {
                        // 加签节点：同意、拒绝、加签人先审、加签人后审、转签、知会
                        respVO.setOperations(CollUtil.newArrayList(AGREE.getValue(), REFUSE.getValue(), SIGN_THEN_APPROVAL.getValue(), APPROVAL_THEN_SIGN.getValue(), ASSIGN.getValue(), NOTICE.getValue()));
                    } else if (continuousSupervisor) {
                        // 连续上级节点：同意、拒绝、加签人先审、加签人后审、转签、知会
                        respVO.setOperations(CollUtil.newArrayList(AGREE.getValue(), REFUSE.getValue(), SIGN_THEN_APPROVAL.getValue(), APPROVAL_THEN_SIGN.getValue(), ASSIGN.getValue(), NOTICE.getValue()));
                    } else if (dynamicChain) {
                        // 动态审批链，调用获取其关联初始节点的操作配置项
                        respVO.setOperations(taskQuery.getOperations());
                    } else {
                        respVO.setOperations(taskQuery.getOperations());
                    }
                    // 页面表单
                    respVO.setFormProperties(taskQuery.getFormProperties());

                    if (!multiTask && !continuousSupervisor && !addSign) {
                        // 正常审批走分支判断
                        respVO.setBranchType(taskQuery.getBranchType());
                        if (taskQuery.getBranchType().equals(BranchType.SINGLE.getValue()) || taskQuery.getBranchType().equals(BranchType.MULTIPLE.getValue())) {
                            // 获取选择分支
                            respVO.setNextSequenceFlows(getNextSequenceFlows(task));

                        } else if (taskQuery.getBranchType().equals(BranchType.AUTO.getValue())) {
                            // 不需要手动选择，此时可以开始确定下一个用户任务是否自选
                            List<NextSequenceFlowVO> nextSequenceFlows = getNextSequenceFlows(task);
                            // 根据接下来的节点判断 当前页面是否需要自选
                            if (CollUtil.isNotEmpty(nextSequenceFlows)) {
                                Optional<NextSequenceFlowVO> any = nextSequenceFlows.stream().filter(e -> e.isOptionalPersonnel()).findAny();
                                respVO.setOptionalPersonnel(any.isPresent());
                            }
                        }
                    }
                }

            }
        }


        return respVO;
    }

    /**
     * 获取任务的审批状态
     * 0自动拾取
     * 1手动拾取-未拾取
     * 2手动拾取-已拾取
     *
     * @param task
     * @param taskCandidateQuery
     * @return int
     * @author zhonglj
     * @date 2024/12/13 8:51
     **/
    private Integer getClaimStatus(Task task, TaskCandidateQuery taskCandidateQuery) {
        if (taskCandidateQuery == null || taskCandidateQuery.isAutoClaim()) {
            return AUTO_CLAIM.getValue();
        }

        // 是否候选任务
        boolean candidate = CollUtil.isNotEmpty(taskCandidateQuery.getCandidateUser()) || CollUtil.isNotEmpty(taskCandidateQuery.getCandidateRole()) || CollUtil.isNotEmpty(taskCandidateQuery.getCandidateLevel()) || CollUtil.isNotEmpty(taskCandidateQuery.getCandidatePosition()) || CollUtil.isNotEmpty(taskCandidateQuery.getCandidateOrg());
        // 是否手动拾取
        boolean needClaim = !taskCandidateQuery.isAutoClaim();
        if (needClaim && candidate) {
            if (StrUtil.isBlank(task.getAssignee())) {
                // 手动拾取-未拾取
                return NEED_CLAIM_YET.getValue();
            } else {
                // 手动拾取-已拾取
                return NEED_CLAIM_HAD.getValue();
            }
        }

        return AUTO_CLAIM.getValue();
    }

    private List<NextSequenceFlowVO> getNextSequenceFlows(Task task) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        String curTaskDefKey = task.getTaskDefinitionKey();
        // 获取当前任务的 FlowNode
        FlowNode curTaskNode = (FlowNode) bpmnModel.getFlowElement(curTaskDefKey);
        // 获取当前任务的所有出站 SequenceFlow
        List<SequenceFlow> nextSequenceFlows = curTaskNode.getOutgoingFlows();

        ProcessEngineConfigurationImpl config = (ProcessEngineConfigurationImpl) processEngine.getProcessEngineConfiguration();
        ExpressionManager expressionManager = config.getExpressionManager();

        List<SequenceFlow> allowedSequenceFlows = nextSequenceFlows.stream().filter(flow -> validateSequenceFlow(flow.getConditionExpression(), task, expressionManager)).collect(Collectors.toList());

        if (CollUtil.isNotEmpty(allowedSequenceFlows)) {
            // 获取分支后面的节点
            List<NextSequenceFlowVO> sequenceFlowVos = allowedSequenceFlows.stream().filter(flow -> flow.getTargetFlowElement() != null).map(flow -> {
                FlowElement next = flow.getTargetFlowElement();
                NextSequenceFlowVO vo = new NextSequenceFlowVO();
                vo.setSequenceId(flow.getId());
                vo.setSequenceName(StrUtil.isNotBlank(flow.getName()) ? flow.getName() : next.getName());
                vo.setActId(next.getId());

                // 获取页面配置的选择分支类型
                ActWorkflowConfigEntity nodeConfig = workflowConfigService.getOne(Wrappers.lambdaQuery(ActWorkflowConfigEntity.class).eq(ActWorkflowConfigEntity::getActId, next.getId()).eq(ActWorkflowConfigEntity::getProcDefId, task.getProcessDefinitionId()).eq(BaseEntity::getDeleted, CommonConstant.YESNO_NO));
                if (nodeConfig != null && StrUtil.isNotBlank(nodeConfig.getJson())) {
                    // 是否自选人员
                    if (next instanceof UserTask) {
                        TaskQuery taskQuery = JSONUtil.toBean(nodeConfig.getJson(), TaskQuery.class);
                        if (taskQuery != null) {
                            vo.setOptionalPersonnel(StrUtil.equals(taskQuery.getDynamicApprovalType(), DynamicApprovalType.OPTIONAL_PERSONNEL.getValue()));
                        }

                    } else {
                        // 非用户任务节点默认为false
                        vo.setOptionalPersonnel(false);
                    }

                }

                return vo;
            }).collect(Collectors.toList());

            return sequenceFlowVos;
        }

        return null;
    }

    private Boolean validateSequenceFlow(String conditionExpression, Task task, ExpressionManager expressionManager) {
        if (conditionExpression == null) {
            return true;
        }

        // 创建表达式
        Expression expression = expressionManager.createExpression(conditionExpression);
        // 计算表达式的值
        VariableScope variableScope = getVariableScopeFromTask(task);
        if (variableScope != null) {
            Object value = expression.getValue(variableScope);
            return ObjectUtil.equal(value, true);
        } else {
            return false;
        }
    }

    /**
     * 根据任务获取VariableScope
     *
     * @param task
     * @return org.activiti.engine.delegate.VariableScope
     * @author zhonglj
     * @date 2024-10-19 16:59
     **/
    private VariableScope getVariableScopeFromTask(Task task) {
        // 获取任务的执行上下文
        if (task instanceof TaskEntity) {
            TaskEntity taskEntity = (TaskEntity) task;
            return taskEntity.getExecution();

        } else {
            // 获取任务所属的流程实例 ID
            String processInstanceId = task.getProcessInstanceId();
            // 获取流程实例的执行上下文
            Execution execution = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).singleResult();
            if (execution instanceof ExecutionEntity) {
                return (ExecutionEntity) execution;

            } else {
                // 获取任务的执行上下文
                Object object = taskService.getVariable(task.getId(), "execution");
                if (object != null) {
                    return (ExecutionEntity) object;
                } else {
                    return null;
                }

            }
        }
    }
}
